﻿using System;
using System.Collections.Generic;
using System.Linq;
using NHibernate.Linq;
using Xenta.Api;
using Xenta.Entities;
using Xenta.Enums;
using Xenta.Security;
using Xenta.Structs;
using Xenta.Utils;

namespace Xenta.Operations
{
    #region __General__

    /// <summary>
    /// Creates a catalog item group.
    /// </summary>
    public class CreateCatalogItemGroup : CreateAuditable<CatalogItemGroupEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("CTLG");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .IsVerified()
                    .Rule((ctx, uow) =>
                    {
                        var manager = ctx.CatalogManager(uow);
                        if(manager == null || !manager.Flags.HasFlag(CatalogManagerFlags.Active))
                            return false;
                        var supplier = uow.Execute(new GetCatalogSupplier
                        {
                            EntityID = SupplierID
                        }).Result;
                        if(supplier == null)
                            return false;
                        return supplier.Managers.Contains(manager);
                    });
            }
        }

        /// <summary>
        /// Gets or sets the supplier identifier.
        /// </summary>
        public int SupplierID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flags.
        /// </summary>
        public CatalogItemGroupFlags Flags
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Construct(IUnitOfWork uow, CatalogItemGroupEntity entity)
        {
            base.Construct(uow, entity);

            entity.Supplier = uow.Execute(new GetCatalogSupplier
            {
                ThrowsException = true,
                EntityID = SupplierID
            }).Result;
            entity.Flags = Flags;
        }

        #endregion
    }

    /// <summary>
    /// Updates the catalog item group.
    /// </summary>
    public class UpdateCatalogItemGroup : UpdateAuditable<CatalogItemGroupEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("CTLG");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .IsVerified()
                    .Rule((ctx, uow) =>
                    {
                        var manager = ctx.CatalogManager(uow);
                        if(manager == null || !manager.Flags.HasFlag(CatalogManagerFlags.Active))
                            return false;
                        var group = uow.Execute(new GetCatalogItemGroup
                        {
                            EntityID = EntityID
                        }).Result;
                        if(group == null)
                            return false;
                        return group.Supplier.Managers
                            .Contains(manager);
                    });
            }
        }

        /// <summary>
        /// Gets or sets the flags.
        /// </summary>
        public CatalogItemGroupFlags Flags
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Revise(IUnitOfWork uow, CatalogItemGroupEntity entity)
        {
            base.Revise(uow, entity);

            entity.Flags = Flags;
        }

        #endregion
    }

    /// <summary>
    /// Deletes the catalog item group.
    /// </summary>
    public class DeleteCatalogItemGroup : DeleteEntity<CatalogItemGroupEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("CTLG");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .IsVerified()
                    .Rule((ctx, uow) =>
                    {
                        var manager = ctx.CatalogManager(uow);
                        if(manager == null || !manager.Flags.HasFlag(CatalogManagerFlags.Active))
                            return false;
                        var group = uow.Execute(new GetCatalogItemGroup
                        {
                            EntityID = EntityID
                        }).Result;
                        if(group == null)
                            return false;
                        return group.Supplier.Managers
                            .Contains(manager);
                    });
            }
        }

        #endregion

        #region Utilities

        protected override void Destruct(IUnitOfWork uow, CatalogItemGroupEntity entity)
        {
            foreach(var i in entity.Translations)
            {
                uow.Execute(new DeleteCatalogItemGroupTranslation
                {
                    EntityID = i.EntityID
                });
            }
            foreach(var i in entity.Items.ToList())
            {
                uow.Execute(new UnmapCatalogItemGroupFromCatalogItem
                {
                    EntityID = i.EntityID,
                    ChildID = entity.EntityID
                });
            }
            base.Destruct(uow, entity);
        }

        #endregion
    }

    /// <summary>
    /// Gets the catalog item group by identifier.
    /// </summary>
    public class GetCatalogItemGroup : GetEntity<CatalogItemGroupEntity>,
        IApiOperation
    {
    }

    #endregion

    #region __Translation__

    /// <summary>
    /// Creates a group translation.
    /// </summary>
    public class CreateCatalogItemGroupTranslation : CreateTranslation
        <TCatalogItemGroupEntity, CatalogItemGroupEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("CTLG");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .IsVerified()
                    .Rule((ctx, uow) =>
                    {
                        var manager = ctx.CatalogManager(uow);
                        if(manager == null || !manager.Flags.HasFlag(CatalogManagerFlags.Active))
                            return false;
                        var group = uow.Execute(new GetCatalogItemGroup
                        {
                            EntityID = HolderID
                        }).Result;
                        if(group == null)
                            return false;
                        return group.Supplier.Managers
                            .Contains(manager);
                    });
            }
        }

        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        public string Name
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Construct(IUnitOfWork uow, TCatalogItemGroupEntity entity)
        {
            base.Construct(uow, entity);

            entity.Name = StringHelper.EnsureNotNull(Name).TrimToLen(100);
        }

        #endregion
    }

    /// <summary>
    /// Updates the group translation.
    /// </summary>
    public class UpdateCatalogItemGroupTranslation : UpdateTranslation
        <TCatalogItemGroupEntity, CatalogItemGroupEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("CTLG");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .IsVerified()
                    .Rule((ctx, uow) =>
                    {
                        var manager = ctx.CatalogManager(uow);
                        if(manager == null || !manager.Flags.HasFlag(CatalogManagerFlags.Active))
                            return false;
                        var translation = uow.Execute(new GetCatalogItemGroupTranslation
                        {
                            EntityID = EntityID
                        }).Result;
                        if(translation == null)
                            return false;
                        return translation.Holder.Supplier
                            .Managers.Contains(manager);
                    });
            }
        }

        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        public string Name
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Revise(IUnitOfWork uow, TCatalogItemGroupEntity entity)
        {
            base.Revise(uow, entity);

            entity.Name = StringHelper.EnsureNotNull(Name).TrimToLen(100);
        }

        #endregion
    }

    /// <summary>
    /// Saves a group translation.
    /// </summary>
    public class SaveCatalogItemGroupTranslation : SaveTranslation
        <TCatalogItemGroupEntity, CatalogItemGroupEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("CTLG");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .IsVerified()
                    .Rule((ctx, uow) =>
                    {
                        var manager = ctx.CatalogManager(uow);
                        if(manager == null || !manager.Flags.HasFlag(CatalogManagerFlags.Active))
                            return false;
                        var group = uow.Execute(new GetCatalogItemGroup
                        {
                            EntityID = HolderID
                        }).Result;
                        if(group == null)
                            return false;
                        return group.Supplier.Managers
                            .Contains(manager);
                    });
            }
        }

        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        public string Name
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Work(IUnitOfWork uow, out TCatalogItemGroupEntity result)
        {
            var translation = uow.Execute(new GetCatalogItemGroupTranslationByLanguage
            {
                HolderID = HolderID,
                LanguageID = LanguageID
            }).Result;
            if(translation == null)
            {
                translation = uow.Execute(new CreateCatalogItemGroupTranslation
                {
                    HolderID = HolderID,
                    LanguageID = LanguageID,
                    Name = Name
                }).Result;
            }
            else
            {
                translation = uow.Execute(new UpdateCatalogItemGroupTranslation
                {
                    EntityID = translation.EntityID,
                    LanguageID = LanguageID,
                    Name = Name
                }).Result;
            }
            result = translation;
        }

        #endregion
    }

    /// <summary>
    /// Deletes the group translation.
    /// </summary>
    public class DeleteCatalogItemGroupTranslation : DeleteTranlsation
        <TCatalogItemGroupEntity, CatalogItemGroupEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("CTLG");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .IsVerified()
                    .Rule((ctx, uow) =>
                    {
                        var manager = ctx.CatalogManager(uow);
                        if(manager == null || !manager.Flags.HasFlag(CatalogManagerFlags.Active))
                            return false;
                        var translation = uow.Execute(new GetCatalogItemGroupTranslation
                        {
                            EntityID = EntityID
                        }).Result;
                        if(translation == null)
                            return false;
                        return translation.Holder.Supplier
                            .Managers.Contains(manager);
                    });
            }
        }

        #endregion
    }

    /// <summary>
    /// Gets the group translation.
    /// </summary>
    public class GetCatalogItemGroupTranslation : GetTranlsation
        <TCatalogItemGroupEntity, CatalogItemGroupEntity>,
        IApiOperation
    {
    }

    /// <summary>
    /// Gets the group translation.
    /// </summary>
    public class GetCatalogItemGroupTranslationByLanguage : GetTranlsationByLanguage
        <TCatalogItemGroupEntity, CatalogItemGroupEntity>,
        IApiOperation
    {
    }

    #endregion

    #region __Search__

    /// <summary>
    /// Search over catalog item group collection.
    /// </summary>
    public class SearchCatalogItemGroups : SearchOperation<CatalogItemGroupEntity>,
        IApiOperation
    {
        #region Ctors

        /// <summary>
        /// Initializes a new class instance.
        /// </summary>
        public SearchCatalogItemGroups()
        {
            Term = SearchTerm.Empty;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the language identifier.
        /// </summary>
        public int? LanguageID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the search term.
        /// </summary>
        public SearchTerm Term
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the supplier identifier.
        /// </summary>
        public int? SupplierID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the supplier identifier.
        /// </summary>
        public int? ItemID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flag mask.
        /// </summary>
        public CatalogItemGroupFlags FlagMask
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override IQueryable<CatalogItemGroupEntity> Build(IUnitOfWork uow, IQueryable<CatalogItemGroupEntity> query)
        {
            query = base.Build(uow, query);
            query = query.Fetch(x => x.Supplier);
            query = query.FilterByFlags(x => (int)x.Flags, (int)FlagMask);
            switch(Term.Mode)
            {
                case SearchTerm.SearchMode.StartsWith:
                    if(LanguageID.HasValue)
                        query = query.Where(x => x.Translations.Any(i =>
                            i.Language.EntityID == LanguageID.Value &&
                            i.Name.StartsWith(Term.Keyword)));
                    else
                        query = query.Where(x => x.Translations.Any(i =>
                            i.Name.StartsWith(Term.Keyword)));
                    break;
                case SearchTerm.SearchMode.EndsWith:
                    if(LanguageID.HasValue)
                        query = query.Where(x => x.Translations.Any(i =>
                            i.Language.EntityID == LanguageID.Value &&
                            i.Name.EndsWith(Term.Keyword)));
                    else
                        query = query.Where(x => x.Translations.Any(i =>
                            i.Name.EndsWith(Term.Keyword)));
                    break;
                case SearchTerm.SearchMode.Contains:
                    if(LanguageID.HasValue)
                        query = query.Where(x => x.Translations.Any(i =>
                            i.Language.EntityID == LanguageID.Value &&
                            i.Name.Contains(Term.Keyword)));
                    else
                        query = query.Where(x => x.Translations.Any(i =>
                            i.Name.Contains(Term.Keyword)));
                    break;
                case SearchTerm.SearchMode.Equals:
                    if(LanguageID.HasValue)
                        query = query.Where(x => x.Translations.Any(i =>
                            i.Language.EntityID == LanguageID.Value &&
                            i.Name.Equals(Term.Keyword)));
                    else
                        query = query.Where(x => x.Translations.Any(i =>
                            i.Name.Equals(Term.Keyword)));
                    break;
                default:
                    if(LanguageID.HasValue)
                        query = query.Where(x => x.Translations.Any(i => i.Language.EntityID == LanguageID.Value));
                    break;
            }
            if(SupplierID.HasValue)
                query = query.Where(x => x.Supplier.EntityID == SupplierID.Value);
            if(ItemID.HasValue)
                query = query.Where(x => x.Items.Any(i => i.EntityID == ItemID.Value));
            return query;
        }

        #endregion
    }

    /// <summary>
    /// Gets the collection of groups as a dictionary, where 
    /// key - group identifier and value - group name.
    /// </summary>
    public class GetCatalogItemGroupDictionary : SearchOperation<TCatalogItemGroupEntity, Int32, String>,
        IApiOperation
    {
        #region Ctors

        /// <summary>
        /// Initializes a new class instance.
        /// </summary>
        public GetCatalogItemGroupDictionary()
        {
            Term = SearchTerm.Empty;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the language identifier.
        /// </summary>
        public int LanguageID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the supplier identifier.
        /// </summary>
        public int? SupplierID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the search term.
        /// </summary>
        public SearchTerm Term
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flag mask.
        /// </summary>
        public CatalogItemGroupFlags FlagMask
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override IQueryable<TCatalogItemGroupEntity> Build(IUnitOfWork uow, IQueryable<TCatalogItemGroupEntity> query)
        {
            query = base.Build(uow, query);
            query = query.FilterByFlags(x => (int)x.Holder.Flags, (int)FlagMask);
            query = query.Where(x => x.Language.EntityID == LanguageID);
            if(SupplierID.HasValue)
                query = query.Where(x => x.Holder.Supplier.EntityID == SupplierID.Value);
            switch(Term.Mode)
            {
                case SearchTerm.SearchMode.StartsWith:
                    query = query.Where(x => x.Name.StartsWith(Term.Keyword));
                    break;
                case SearchTerm.SearchMode.EndsWith:
                    query = query.Where(x => x.Name.EndsWith(Term.Keyword));
                    break;
                case SearchTerm.SearchMode.Contains:
                    query = query.Where(x => x.Name.Contains(Term.Keyword));
                    break;
                case SearchTerm.SearchMode.Equals:
                    query = query.Where(x => x.Name.Equals(Term.Keyword));
                    break;
            }
            query = query.OrderBy(x => x.Name);
            return query;
        }

        protected override IQueryable<KeyValuePair<Int32, String>> Map(IQueryable<TCatalogItemGroupEntity> query)
        {
            return query.Select(x => new KeyValuePair<Int32, String>(x.Holder.EntityID, x.Name));
        }

        #endregion
    }

    #endregion
}
